/*
 * @Author: soso
 * @Date: 2022-01-29 15:15:04
 * @LastEditTime: 2022-02-09 16:44:38
 * @LastEditors: Please set LastEditors
 * @Description: 监控目录
 * @FilePath: /sync-client/sonotify/sonotify.go
 */
package sonotify

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/fsnotify/fsnotify"
)

const (
	ActCreate = "CREATE"
	ActRemove = "REMOVE"
	ActRename = "RENAME"
	ActWrite  = "WRITE"
)

type Watch struct {
	watch *fsnotify.Watcher
}

func NewDirWatch() (watch *Watch, err error) {
	watch = &Watch{}
	watch.watch, err = fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	return watch, nil
}

//监控目录
func (w *Watch) WatchDir(dir string, cbfunc func(act fsnotify.Op, filePath string)) {
	//通过Walk来遍历目录下的所有子目录
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		//这里判断是否为目录，只需监控目录即可
		//目录下的文件也在监控范围内，不需要我们一个一个加
		if info.IsDir() {
			path, err := filepath.Abs(path)
			if err != nil {
				return err
			}
			err = w.watch.Add(path)
			if err != nil {
				return err
			}
			// fmt.Println("监控 : ", path)
		}
		return nil
	})
	go func() {
		for {
			select {
			case ev := <-w.watch.Events:
				{
					if ev.Op&fsnotify.Create == fsnotify.Create {
						// fmt.Println("创建文件 : ", ev.Name)

						if cbfunc != nil {
							cbfunc(fsnotify.Create, ev.Name)
						}
						//这里获取新创建文件的信息，如果是目录，则加入监控中
						fi, err := os.Stat(ev.Name)
						if err == nil && fi.IsDir() {
							w.watch.Add(ev.Name)
							// fmt.Println("添加监控 : ", ev.Name)
						}
					}
					if ev.Op&fsnotify.Write == fsnotify.Write {
						// fmt.Println("写入文件 : ", ev.Name)

						if cbfunc != nil {
							cbfunc(fsnotify.Write, ev.Name)
						}
					}
					if ev.Op&fsnotify.Remove == fsnotify.Remove {
						// fmt.Println("删除文件 : ", ev.Name)

						if cbfunc != nil {
							cbfunc(fsnotify.Remove, ev.Name)
						}
						//如果删除文件是目录，则移除监控
						fi, err := os.Stat(ev.Name)
						if err == nil && fi.IsDir() {
							w.watch.Remove(ev.Name)
							// fmt.Println("删除监控 : ", ev.Name)
						}
					}
					if ev.Op&fsnotify.Rename == fsnotify.Rename {
						// fmt.Println("重命名文件 : ", ev.Name)

						if cbfunc != nil {
							cbfunc(fsnotify.Rename, ev.Name)
						}

						//如果重命名文件是目录，则移除监控
						//注意这里无法使用os.Stat来判断是否是目录了
						//因为重命名后，go已经无法找到原文件来获取信息了
						//所以这里就简单粗爆的直接remove好了
						w.watch.Remove(ev.Name)
					}
					if ev.Op&fsnotify.Chmod == fsnotify.Chmod {
						// fmt.Println("修改权限 : ", ev.Name)

						if cbfunc != nil {
							cbfunc(fsnotify.Chmod, ev.Name)
						}
					}
				}
			case err := <-w.watch.Errors:
				{
					fmt.Println("watch error : ", err)
					return
				}
			}
		}
	}()
}

// 清除所有监控
func (w *Watch) Close() error {
	return w.watch.Close()
}
